/*
* Copyright (C) 2010-2012 Magnus Skjegstad
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
* 
* The latest version of this library can be downloaded from http://mist-pubsub.googlecode.com
* 
*/

package com.skjegstad.mist2.demos;

import com.skjegstad.mist2.IMistSubscriptionListener;
import com.skjegstad.mist2.MistCoreManager;
import com.skjegstad.mist2.MistUtil;
import com.skjegstad.mist2.datatypes.DataElement;
import com.skjegstad.mist2.datatypes.NodeIdentifier;
import com.skjegstad.mist2.datatypes.TopicIdentifier;
import com.skjegstad.mist2.exceptions.MistException;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;

/**
 *
 * @author Magnus Skjegstad
 */
public class TestKmlProducer {
    static public void main(String[] argv) throws UnknownHostException, MistException, SocketException, InterruptedException {
         /*if (webServer != null) {
            webServer.stop();
            webServer = null;
        }
        try {
            webServer = new MistSDWebServer(server, 8080);
        } catch (MistSDWebException ex) {
            JOptionPane.showMessageDialog(this, "Unable to start web server: " + ex.getMessage());
            webServer = null;
            return;
        }

        try {
            webServer.start();
        } catch (MistSDWebException ex) {
            JOptionPane.showMessageDialog(this, "Unable to start web server: " + ex.getMessage());
            return;
        }*/
        
        MistCoreManager server = new MistCoreManager();
        server.setDebug(true);
        server.enableMulticast(InetAddress.getByName("224.0.0.1"), 1237, MistUtil.askUserForNetworkInterface(), 5000);
        MistSDWebServer webServer = new MistSDWebServer(server, 8123);
        System.out.println("Listening on port 8123");
        server.start();
        webServer.start();
        
        Thread.sleep(10000);
    }
}


class MistSDWebServer {
    
    protected int listenPort;
    protected HttpServer server;
    protected final Map<String, HttpHandler> context;
    protected final MistCoreManager mdistServer;

    public MistSDWebServer(MistCoreManager mdistServer, int listenPort) throws MistException {
        if (mdistServer == null && mdistServer.isStarted())
            throw new MistException("Mist server must be running.");

        this.mdistServer = mdistServer;
        this.listenPort = listenPort;
        this.context = new ConcurrentHashMap<String, HttpHandler>();

        this.addHandler("/", new MistSDWebOverviewHandler(this));
        this.addHandler("/test", new MistSDWebTestpageHandler());
        this.addHandler("/test.kml", new MistSDWebKMLOutputHandler(mdistServer));
        this.addHandler("/camera.jpg", new MistPictureHandler(mdistServer));
        this.addHandler("/camera.html", new MistPicturePageHandler());
    }

    public void addHandler(String context, HttpHandler handler) {
        synchronized(context) {
            this.context.put(context, handler);
        }
    }

    public Map<String, HttpHandler> getHandlers() {
        synchronized(context) {
            return new ConcurrentHashMap<String, HttpHandler>(this.context);
        }
    }
    
    protected void init() throws MistException {
        server = null;

        if (this.context == null || this.context.size() == 0)
            throw new MistException("Web server has no content");

        InetSocketAddress addr = new InetSocketAddress(listenPort);
        try {
            server = HttpServer.create(addr, 0);
        } catch (IOException ex) {
            throw new MistException("Unable to create HttpServer instance", ex);
        }

        for (Entry<String, HttpHandler> e : this.context.entrySet()) {
            server.createContext(e.getKey(), e.getValue());
        }

        server.setExecutor(Executors.newCachedThreadPool());
    }

    public void start() throws MistException {
        this.init();
        server.start();
    }

    public void stop() {
        server.stop(3);        
    }
}

class MyHandler implements HttpHandler {

    public void handle(HttpExchange exchange) throws IOException {
        String requestMethod = exchange.getRequestMethod();
        if (requestMethod.equalsIgnoreCase("GET")) {
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/plain");
            exchange.sendResponseHeaders(200, 0);

            OutputStream responseBody = exchange.getResponseBody();
            Headers requestHeaders = exchange.getRequestHeaders();
            Set<String> keySet = requestHeaders.keySet();
            Iterator<String> iter = keySet.iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                List values = requestHeaders.get(key);
                String s = key + " = " + values.toString() + "\n";
                responseBody.write(s.getBytes());
            }
            responseBody.close();
        }
    }
}

class MistSDWebKMLOutputHandler implements HttpHandler {
    private final MistCoreManager server;
    private final ConcurrentHashMap<NodeIdentifier, DataElement> tracks;
    
    public MistSDWebKMLOutputHandler(MistCoreManager server) {
        this.server = server;
        tracks = new ConcurrentHashMap();
        server.subscribe(new TopicIdentifier("no.ffi.tracking.location"), new IMistSubscriptionListener() {
            public void deliverMessage(DataElement message) throws MistException {
               tracks.put(message.getSourceNode(), message);
            }
        });
    }

    protected void addPlacemark(PrintStream out, String styleUrl, String name, double lat, double lon, String description) {
        String styleText = "";
        if (styleUrl != null)
            styleText = "\t\t<styleUrl>" + styleUrl + "</styleUrl>\n";
        String descrText = "";
        if (description != null)
            descrText = "\t\t<description><![CDATA[" + description + "]]></description>\n";

        out.println("\t<Placemark>\n" +
                "\t\t<name>" + name + "</name>\n" + styleText + descrText +
                "\t\t<Point>\n" +
                "\t\t\t<coordinates>" + lon + ", " + lat + "</coordinates>\n" +
                "\t\t</Point>\n" +
                "\t</Placemark>");
    }

    // other tracking icon:
    // "<href>http://maps.google.com/mapfiles/ms/micons/man.png</href>"+
    
    protected void addStyles(PrintStream out) {
        out.println("<Style id=\"tracking\">"+
      "<IconStyle>"+
        "<scale>0.5</scale>"+
        "<Icon>"+
          "<href>http://upload.wikimedia.org/wikipedia/commons/e/ea/APP-6a_Friendly.png</href>"+
        "</Icon>"+
      "</IconStyle>"+
    "</Style>");

        out.println("<Style id=\"observation\">"+
      "<IconStyle>"+
        "<Icon>"+
          "<href>http://maps.google.com/mapfiles/kml/pal4/icon49.png</href>"+
        "</Icon>"+
      "</IconStyle>"+
    "</Style>");

        
    }
    
    public void handle(HttpExchange he) throws IOException {
        String requestMethod = he.getRequestMethod();
        if (requestMethod.equalsIgnoreCase("GET")) {
            Headers responseHeaders = he.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/plain");
            he.sendResponseHeaders(200, 0);

            PrintStream responseBody = new PrintStream(he.getResponseBody());
            
            responseBody.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"\n"+
                                "\txmlns:gx=\"http://www.google.com/kml/ext/2.2\">");

            responseBody.println("<Document>\n"+
                    "\t<name>Document.kml</name>\n" +
                    "\t<open>1</open>\n" +
                    "\t<Style id=\"exampleStyle\">\n" +
                    "\t\t<LabelStyle>\n" +
                    "\t\t\t<color>ff0000cc</color>\n" +
                    "\t\t</LabelStyle>\n" +
                    "\t</Style>\n");


            addStyles(responseBody);

          /*  // add observations
            for (ObservationAdvertisement obs : server.getObservationAds()) {
                ObservationObject o = obs.getObservation();
                if (o != null) {
                        String description = "Observed by " + obs.getId() + "<br/>" +
                                "Age: " + o.getAge() + "<br/>";
                        // TODO vis jpeg
                        /*if (o.getData() != null) {
                            byte[] data = server.getObservationData(o.getData());
                            if (data != null)
                                description = description + "Data: " + new String(data, charset);
                        } --
                        addPlacemark(responseBody, "observation", o.getObservedId(), o.getLat(), o.getLon(),
                                description);                        
                    }

                }*/

            // add tracking information
            for (DataElement de : tracks.values()) {
                 ByteArrayInputStream bis = new ByteArrayInputStream(de.getPayload());
                        DataInputStream dis = new DataInputStream(bis);
                        try {
                            String s = "";
                            s = s + "id: " + de.getSourceNode().getNodeIdentifier() + " ";
                            long node = de.getSourceNode().getNodeIdentifier().longValue();
                                                        
                            s = s + "t:" + dis.readLong() + " ";
                            
                            double lat = dis.readDouble();
                            s = s + "lat: " + lat + " ";
                            double lon = dis.readDouble();
                            s = s + "lon: " + lon + " ";
                            double alt = dis.readDouble();
                            s = s + "alt: " + alt + " ";
                            float speed = dis.readFloat();
                            s = s + "speed: " + speed + " ";
                            s = s + "bearing: " + dis.readFloat();
                            System.out.println("*** Mobile node:  " + s);
                            
                            addPlacemark(responseBody, "tracking", "Mobile node",  lat, lon, Long.toString(node));
                        } catch (Exception ex) {
                            System.err.println ("Unable to parse location: " + ex.getMessage());
                        }
            }
                
            /*for (TrackingAdvertisement t : server.getTrackingAds()) {
                    String description = null;                    
                    ServiceAdvertisement serviceAd = server.getServiceAd(t.getId());
                    if (serviceAd != null) {
                        description = "<h3>Services</h3>" +
                                "<table><tr><td>Service</td><td>Port type</td><td>End point</td></tr>";
                            for (ServiceEndpointObject sep : serviceAd.getEndpoints())
                                description = description +
                                    "<tr><td>" + sep.getName() + "</td><td>" + sep.getPortType() + "</td><td>" + sep.getEndpoint() + "</td></tr>";                        
                        description = description + "</table>";
                    }

                    addPlacemark(responseBody, "tracking", t.getId(), t.getLat(), t.getLon(), description);
                }*/

            responseBody.println("</Document>");
            responseBody.println("</kml>");

            responseBody.close();
        }
    }

}

class MistSDWebOverviewHandler implements HttpHandler {
    protected final MistSDWebServer parentServer;

    public MistSDWebOverviewHandler(MistSDWebServer parentServer) {
        this.parentServer = parentServer;
    }

    /**
     * Displays an overview of registered context paths (valid URLs)
     * @param exchange
     * @throws IOException
     */
    public void handle(HttpExchange exchange) throws IOException {
        String requestMethod = exchange.getRequestMethod();
        if (requestMethod.equalsIgnoreCase("GET")) {
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/html");
            exchange.sendResponseHeaders(200, 0);

            OutputStream responseBody = exchange.getResponseBody();

            responseBody.write("<html><body>".getBytes());
            responseBody.write("<table>".getBytes());
            for (Entry <String, HttpHandler> e : parentServer.getHandlers().entrySet()) {
                String s = "<tr><td><a href=\"" + e.getKey() + "\">" + e.getValue().getClass().getName() + "</a></td><td>" + e.getKey() + "</td></tr>\n";
                responseBody.write(s.getBytes());
            }
            responseBody.write("</table></body></html>".getBytes());
            
            responseBody.close();
        }
    }

}

class MistPictureHandler implements HttpHandler {
    protected final MistCoreManager parentServer;
    protected DataElement lastImage;

    public MistPictureHandler(MistCoreManager server) {
        this.parentServer = server;
        
        server.subscribe(new TopicIdentifier("no.ffi.pictures"), new IMistSubscriptionListener() {
            public void deliverMessage(DataElement message) throws MistException {
                System.err.println("new picture from " + message.getSourceNode() + ", seq " + message.getSeqNr());
                if (lastImage == null || 
                        (!lastImage.getSourceNode().equals(message.getSourceNode())) || 
                        (lastImage.getSeqNr() < message.getSeqNr())) 
                            lastImage = message;
                else
                    System.err.println("got picture, but was not for us, seq " + message.getSeqNr());
            }
        });
    }

    /**
     * Displays an overview of registered context paths (valid URLs)
     * @param exchange
     * @throws IOException
     */
    public void handle(HttpExchange exchange) throws IOException {
        String requestMethod = exchange.getRequestMethod();
        if (requestMethod.equalsIgnoreCase("GET")) {
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "image/jpeg");
            responseHeaders.set("Cache-Control", "no-cache, must-revalidate");
            Calendar cal = Calendar.getInstance();          
            responseHeaders.set("Last-Modified", cal.getTime().toGMTString());
            exchange.sendResponseHeaders(200, 0);

            OutputStream responseBody = exchange.getResponseBody();

            responseBody.write(lastImage.getPayload());
            
            responseBody.close();
        }
    }

}
class MistPicturePageHandler implements HttpHandler {

    public MistPicturePageHandler() {        

    }

    /**
     * Displays an overview of registered context paths (valid URLs)
     * @param exchange
     * @throws IOException
     */
    public void handle(HttpExchange exchange) throws IOException {
        String requestMethod = exchange.getRequestMethod();
        if (requestMethod.equalsIgnoreCase("GET")) {
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/html");
            exchange.sendResponseHeaders(200, 0);

            PrintStream responseBody = new PrintStream(exchange.getResponseBody());

            responseBody.println("<html><head><meta http-equiv=\"pragma\" content=\"no-cache\" />"+
                    "<meta http-equiv=\"refresh\" content=\"5;URL=/camera.html\" /></head>");
            responseBody.println("<body><img src=\"/camera.jpg\"/></body></html>");
            
            responseBody.close();
        }
    }

}







class MistSDWebTestpageHandler implements HttpHandler {
    /**
     * Prints headers. 
     * @param exchange
     * @throws IOException
     */
    public void handle(HttpExchange exchange) throws IOException {
        String requestMethod = exchange.getRequestMethod();
        if (requestMethod.equalsIgnoreCase("GET")) {
            Headers responseHeaders = exchange.getResponseHeaders();
            responseHeaders.set("Content-Type", "text/plain");
            exchange.sendResponseHeaders(200, 0);

            OutputStream responseBody = exchange.getResponseBody();
            Headers requestHeaders = exchange.getRequestHeaders();
            Set<String> keySet = requestHeaders.keySet();
            Iterator<String> iter = keySet.iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                List values = requestHeaders.get(key);
                String s = key + " = " + values.toString() + "\n";
                responseBody.write(s.getBytes());
            }
            responseBody.close();
        }
    }
}